package com.inspection.service.impl;

import com.inspection.constant.MenuTypeEnum;
import com.inspection.dao.SysMenuDao;
import com.inspection.dao.SysRoleMenuDao;
import com.inspection.dao.SysUserRoleDao;
import com.inspection.entity.SysMenuDO;
import com.inspection.entity.SysRoleMenuDO;
import com.inspection.entity.SysUserDO;
import com.inspection.entity.SysUserRoleDO;
import com.inspection.model.dto.SysMenuDTO;
import com.inspection.model.dto.SysMenuQueryDTO;
import com.inspection.model.vo.UserMenuVO;
import com.inspection.service.SysMenuService;
import com.inspection.support.AssertUtils;
import com.inspection.support.auth.AuthHolder;
import com.inspection.support.model.BusinessBaseException;
import com.inspection.support.model.MenuDataTree;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zhaoqi
 * @since 2020-12-04
 */
@Service
public class SysMenuServiceImpl implements SysMenuService {

    @Autowired
    private SysMenuDao sysMenuDao;

    @Autowired
    private SysUserRoleDao sysUserRoleDao;

    @Autowired
    private SysRoleMenuDao sysRoleMenuDao;

    @Override
    public List<SysMenuDO> queryMenu(SysMenuQueryDTO query) {
        Sort sort = new Sort(Sort.Direction.DESC, "id");
        return sysMenuDao.findAll(sort);
    }

    @Override
    public MenuDataTree getMenuManagerTree() {
        List<SysMenuDO> menus = queryMenu(new SysMenuQueryDTO());

        //先构建根结点
        MenuDataTree.TreeNode rootNode = buildRootNode();


        //根据根结点循环获取下面的结点
        Map<Long, List<SysMenuDO>> parentMap = menus.stream().collect(Collectors.groupingBy(SysMenuDO::getParentId));

        findDeep(rootNode, parentMap);


        MenuDataTree dataTree = new MenuDataTree();
        dataTree.setRootNode(rootNode);
        dataTree.setTreeName("菜单树");

        return dataTree;
    }

    @Override
    public void saveMenu(SysMenuDTO save) {

        AssertUtils.nonNull(save.getParentId(),"parentId 不能为空");
        AssertUtils.nonNull(save.getType(),"type 不能为空");
        if (save.getType() != MenuTypeEnum.CATEGORY.getType()) {
            AssertUtils.nonNull(save.getPerms(), "perms 不能为空");
            SysMenuDO menuQuery = new SysMenuDO();
            menuQuery.setPerms(save.getPerms());

            Optional<SysMenuDO> menuOp = sysMenuDao.findOne(Example.of(menuQuery));
            if (menuOp.isPresent()) {
                throw new BusinessBaseException("授权信息重复");
            }
        }


        SysMenuDO menuSave = new SysMenuDO();
        BeanUtils.copyProperties(save,menuSave);
        sysMenuDao.saveAndFlush(menuSave);


    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delMenu(SysMenuDTO del) {
       AssertUtils.nonNull(del.getId(),"菜单id不能为空");

        Optional<SysMenuDO> menuOp = sysMenuDao.findById(del.getId());
        if (!menuOp.isPresent()) {
            throw new BusinessBaseException("菜单不存在");
        }
        //判断是否还存在子节点
        SysMenuDO childMenuQuery = new SysMenuDO();
        childMenuQuery.setParentId(menuOp.get().getId());
        List<SysMenuDO> childMenus = sysMenuDao.findAll(Example.of(childMenuQuery));
        if(!CollectionUtils.isEmpty(childMenus)){
            throw new BusinessBaseException("需要先删除子目录/菜单,才能删除当前的目录/菜单");
        }
        SysMenuDO sysMenuDO = menuOp.get();
        sysMenuDao.delete(sysMenuDO);

        //删除和角色关联的中间数据
        //删除roleMenu
        SysRoleMenuDO roleMenuQuery = new SysRoleMenuDO();
        roleMenuQuery.setMenuId(sysMenuDO.getId());
        List<SysRoleMenuDO> roleMenus = sysRoleMenuDao.findAll(Example.of(roleMenuQuery));
        if(!CollectionUtils.isEmpty(roleMenus)){
            sysRoleMenuDao.deleteInBatch(roleMenus);
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateMenu(SysMenuDTO update) {
        AssertUtils.nonNull(update.getId(), "菜单id不能为空");

        Optional<SysMenuDO> menuOp = sysMenuDao.findById(update.getId());
        if (!menuOp.isPresent()) {
            throw new BusinessBaseException("菜单不存在");
        }
        SysMenuDO menuUpdate = menuOp.get();
        if (!Objects.equals(menuUpdate.getParentId(), update.getParentId())) {
            //层级存在修改
            Optional<SysMenuDO> pSysMenu = sysMenuDao.findById(update.getParentId());
            if(!pSysMenu.isPresent()){
                throw new BusinessBaseException("要修改的父节点不存在");
            }
            checkNode(MenuTypeEnum.get(menuUpdate.getType()), MenuTypeEnum.get(pSysMenu.get().getType()));
            menuUpdate.setParentId(update.getParentId());
        }


        menuUpdate.setName(update.getName());
        menuUpdate.setIcon(update.getIcon());
        menuUpdate.setOrderNum(update.getOrderNum());
        menuUpdate.setPerms(update.getPerms());
        menuUpdate.setUrl(update.getUrl());
        sysMenuDao.saveAndFlush(menuUpdate);

    }

    @Override
    public UserMenuVO obtainUserAuth() {

        MenuDataTree.TreeNode rootNode = buildRootNode();
        MenuDataTree dataTree = new MenuDataTree();
        dataTree.setRootNode(rootNode);
        dataTree.setTreeName("菜单");



        SysUserDO sysUser = AuthHolder.getSysUser();
        //获取role
        SysUserRoleDO roleQuery = new SysUserRoleDO();
        roleQuery.setUserId(sysUser.getId());
        List<SysUserRoleDO> userRoles = sysUserRoleDao.findAll(Example.of(roleQuery));
        if(CollectionUtils.isEmpty(userRoles)){
            return UserMenuVO.builder().menuList(new ArrayList<>()).build();
        }

        //查询角色对应的权限
        List<Long> roleIdList = userRoles.stream().map(u -> u.getRoleId()).collect(Collectors.toList());


        List<SysRoleMenuDO> roleMenus = sysRoleMenuDao.findAllByRoleIdIn(roleIdList);
        if(CollectionUtils.isEmpty(roleMenus)){
            return UserMenuVO.builder().menuList(new ArrayList<>()).build();
        }

        List<Long> menuIdList = roleMenus.stream().map(r -> r.getMenuId()).distinct().collect(Collectors.toList());
        List<SysMenuDO> menus = sysMenuDao.findAllById(menuIdList);


//        //根据根结点循环获取下面的结点
//        Map<Long, List<SysMenuDO>> parentMap = menus.stream().collect(Collectors.groupingBy(SysMenuDO::getParentId));
//
//        findDeep(rootNode, parentMap);

        return UserMenuVO.builder().menuList(menus).build();
    }

    public static void findDeep(MenuDataTree.TreeNode node, Map<Long, List<SysMenuDO>> parentMap) {
        List<SysMenuDO> menus = parentMap.get(node.getId());
        if (CollectionUtils.isEmpty(menus)) {
            return;
        }
        List<MenuDataTree.TreeNode> dos = new ArrayList<>(menus.size());

        for (SysMenuDO regulationDO : menus) {
            MenuDataTree.TreeNode treeNode = new MenuDataTree.TreeNode();
            BeanUtils.copyProperties(regulationDO, treeNode);
            dos.add(treeNode);
            findDeep(treeNode, parentMap);
        }
        Collections.sort(dos, (o1, o2) -> {
            if (Objects.isNull(o1.getOrderNum()) || Objects.isNull(o2.getOrderNum())) {
                return 0;
            }
            return o1.getOrderNum().compareTo(o2.getOrderNum());
        });

        node.setChildren(dos);

    }

    public static  MenuDataTree.TreeNode buildRootNode(){
        MenuDataTree.TreeNode rootNode = new MenuDataTree.TreeNode();
        SysMenuDO rootMenu = new SysMenuDO();
        rootMenu.setId(-1L);
        rootMenu.setName("菜单");
        rootMenu.setType(MenuTypeEnum.CATEGORY.getType());
        BeanUtils.copyProperties(rootMenu, rootNode);
        return rootNode;
    }

    public void  checkNode(MenuTypeEnum targetType,MenuTypeEnum parentType){
        switch (parentType) {
            case CATEGORY:
                //目录下面啥都可以
                break;
            case MENU:
                if (MenuTypeEnum.BUTTON != targetType) {
                    throw new BusinessBaseException("菜单下面只能存在按钮");
                }
                break;
            case BUTTON:
                throw new BusinessBaseException("按钮已经是最下级权限");

        }


    }

}
